package org.example.spring;

import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

public class LeLouchApplicationContext {
//    配置类
    private Class configClass;

//    存储bean的元数据和自身数据，但未实例化bean
    private ConcurrentHashMap<String,BeanDefinition> beanDefinitions = new ConcurrentHashMap<>();
//    单例池
    private ConcurrentHashMap<String,Object> singletionObjects = new ConcurrentHashMap<>();

    //增强器，需要在所有bean执行完后才会执行，所以暂时保存在List中等在其他任务执行完成
    private final ArrayList<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

    //容器的初始化
    public LeLouchApplicationContext(Class configClass)  {
        this.configClass = configClass;
        if (configClass.isAnnotationPresent(ComponentScan.class)){
            ComponentScan componentAnnotation = (ComponentScan)configClass.getAnnotation(ComponentScan.class);
            String path= componentAnnotation.value();
            path=path.replace(".","/");
            ClassLoader classLoader = LeLouchApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(path);

            File file = new File(resource.getFile());
            if (file.isDirectory()){
                File[] files = file.listFiles();
                for (File f : files) {
                    String fileName = f.getAbsolutePath();
                    if (fileName.endsWith(".class")){
                        String clazzName = fileName.substring(Objects.requireNonNull(classLoader.getResource("")).getFile().length()-1,fileName.length()-6);
                        clazzName=clazzName.replace("\\",".");

                        Class<?> clazz;
                        try {
                            clazz = classLoader.loadClass(clazzName);

                            if (clazz.isAnnotationPresent(Component.class)){

                                //使用instanceof只能针对一个已经实例化的对象进行判断
                                //但是此时我们需要进行判断的是class对象，所以需要使用另一种方式
                                if(BeanPostProcessor.class.isAssignableFrom(clazz)){
                                    //将增强器实例化，并暂时放入List中
                                    BeanPostProcessor instance = (BeanPostProcessor)clazz.newInstance();
                                    beanPostProcessorList.add(instance);
                                }
                                Component component = clazz.getAnnotation(Component.class);
                                //获取指定beanName
                                String beanName =  component.value();
                                //默认beanName实现
                                if("".equals(beanName)){
                                    beanName = Introspector.decapitalize(clazz.getSimpleName());
                                }
                                BeanDefinition definition = new BeanDefinition();
                                if (clazz.isAnnotationPresent(Scope.class)){
                                    Scope annotation = clazz.getAnnotation(Scope.class);
                                    definition.setScope(annotation.value());
                                }else{
                                    definition.setScope("singleton");
                                }
                                definition.setClazz(clazz);
                                beanDefinitions.put(beanName,definition);
                            }
                        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
                            throw new RuntimeException(e);
                        }

                    }
                }
            }
        }
        for (String beanName : beanDefinitions.keySet()) {
            BeanDefinition definition = beanDefinitions.get(beanName);
            if ("singleton".equals(definition.getScope())){
                Object bean = createBean(beanName, definition);
                singletionObjects.put(beanName,bean);
            }
        }
    }
    private Object createBean(String beanName,BeanDefinition beanDefinition){
        Class clazz = beanDefinition.getClazz();
        Object  instance = null;
        try {
            instance = clazz.getConstructor().newInstance();


            //Autowired实现代码
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(Autowired.class)){
                    field.setAccessible(true);
                    Object bean = getBean(field.getName());
                    if(bean!=null){
//                        ByName
                        field.set(instance,getBean(field.getName()));
                    }else{
                        //ByType
                    }
                }



            }
            //Aware回调机制
            if (instance instanceof BeanNameAware){
                ((BeanNameAware)instance).setBeanName(beanName);
            }

            //初始化前
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance=beanPostProcessor.postProcessBeforeInitialization(beanName,instance);
            }
            //生命周期中的初始化，如果bean实现了Initializing接口，就会执行afterPropertiesSet方法
            if (instance instanceof InitializingBean){

                ((InitializingBean)instance).afterPropertiesSet();
            }
            //初始化后
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance=beanPostProcessor.postProcessAfterInitialization(beanName,instance);
            }
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
        return instance;
    }
    public Object getBean(String beanName){
        BeanDefinition beanDefinition = beanDefinitions.get(beanName);
        if (beanDefinition==null){
            throw new NullPointerException();
        }else{
            String scope = beanDefinition.getScope();
            if ("singleton".equals(scope)){
                Object bean = singletionObjects.get(beanName);

                //如果还未出现在单例池中，尝试放入单例池
                if (bean==null){
                    Object bean1 = createBean(beanName, beanDefinition);
                    singletionObjects.put(beanName,bean1);
                }
                return bean;
            }else{
                return createBean(beanName, beanDefinition);
            }

        }

    }


    public <T> T getBean(String beanName, Class<T> clazz){
        return (T)getBean(beanName);
    }
}
